package com.golxen.framework.netty.session.agent;


import com.alibaba.fastjson.JSON;
import com.golxen.framework.CrossProtocol;
import com.golxen.framework.GameProtocol;
import com.golxen.framework.PBPacket.PBPacket;
import com.golxen.framework.PBPacket.PBPacketUtils;
import com.golxen.framework.activity.CrossActivityProcessMgr;
import com.golxen.framework.common.BuiltinPlugin;
import com.golxen.framework.common.server.eServerType;
import com.golxen.framework.common.server.agent.AgentPlayer;
import com.golxen.framework.common.server.agent.AgentPlayerMgr;
import com.golxen.framework.common.server.game.dao.CrossServerInfoDaoImpl;
import com.golxen.framework.common.server.game.dao.entity.CrossServerInfo;
import com.golxen.framework.common.server.game.dao.entity.ProcessInfo;
import com.golxen.framework.executor.ThreadPlugin;
import com.golxen.framework.netty.NettyServices;
import com.golxen.framework.netty.session.base.NettyChannelSession;
import com.golxen.protojava.CrossProto;
import com.golxen.protojava.LoadProto;
import com.golxen.utils.LogUtil;
import com.golxen.utils.YmlUtil;
import com.google.protobuf.InvalidProtocolBufferException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;

import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ServerClientMgr {

    private static Logger logger = LogUtil.COMMON;
    private static Map<eServerType, Map<Integer, ServerClientGroup>> allConnectSetMap;

    public static boolean init() {
        try {
            // 连接服务器
            connectAllServer();
//            // 连接所有跨服
//            connectCross();
        } catch (Exception e) {
            logger.error("", e);
            return false;
        }

        return true;
    }

    private static void connectAllServer() {
        allConnectSetMap = new ConcurrentHashMap<>();
        allConnectSetMap.put(eServerType.GAME, new HashMap<>());
        allConnectSetMap.put(eServerType.CROSS, new HashMap<>());
        for (eServerType serverType : allConnectSetMap.keySet()) {
            if (serverType==eServerType.GAME)
                connectGame(serverType);
            if (serverType==eServerType.CROSS)
                connectAllCross();
        }
    }

    private static void connectAllCross() {
        int channelId = YmlUtil.getInt("channelId");
        List<CrossServerInfo> crossServerInfoList = new CrossServerInfoDaoImpl()
                .getAllChannelCrossServerInfo(channelId);


        for (CrossServerInfo crossServerInfo : crossServerInfoList) {
            for (ProcessInfo info : crossServerInfo.getProcessInfoList()) {
                if (info == null) {
                    continue;
                }

                if (StringUtils.isBlank(info.getServerName())) {
                    continue;
                }

                if (!info.getServerName().startsWith("cross_server")) {
                    continue;
                }

                LogUtil.COMMON.info("====连接全服跨 {}", JSON.toJSONString(info));
                connect1(crossServerInfo, info);

            }
        }

        LogUtil.COMMON.info("connect cross over");

    }

    private static void connect1(CrossServerInfo crossServerInfo, ProcessInfo info) {
        eServerType serverType = eServerType.CROSS;

        LogUtil.COMMON.info("connect serverType={} id={}", serverType, crossServerInfo.getCrossServerId());

        ThreadPlugin.getTimerExecutorService().execute(() -> {
            ServerClientGroup sessionGroup = connectCross(info, serverType);
            synchronized (allConnectSetMap) {
                allConnectSetMap.get(serverType).put(crossServerInfo.getCrossServerId(), sessionGroup);
            }
        });
    }


    private static ServerClientGroup connectCross(ProcessInfo info, eServerType serverType) {
        int serverId = info.getServerId();
        InetSocketAddress address = new InetSocketAddress(info.getAddress(), info.getPort());
        ServerClientGroup sessionGroup = new ServerClientGroup(address, null, NettyServices.LINK_COUNT,
                 serverType,info);
        sessionGroup.setServerId(serverId);
        boolean hasFailed = false;
        for (int i = 0; i < NettyServices.LINK_COUNT; i++) {
            try {
                NettyChannelSession session = sessionGroup.connect(i);
                if (session == null) {
                    logger.error("connect cross " + info.getAddress() + " , i = " + i + " fail. ");
                    hasFailed = true;
                    continue;
                }
                session.getChannel().attr(NettyServices.LINKED_CLIENT).set(sessionGroup);  //map里的值指向对象指针所有不用担心每个map不一样。
                sendRegisterServer(session, i, serverType);
                session.setCrossCheckAliveTime(System.currentTimeMillis());
            } catch (Exception e) {
                hasFailed = true;
                logger.error("connect cross " + info.getAddress() + " , i = " + i + " error. ", e);
            }
        }
        if (hasFailed) {
            ServerClientReConnectHelper.tryConnect(sessionGroup, 10);
        } else {
            logger.info("connect cross success：" + info.getAddress());
        }
        return sessionGroup;
    }

    private static void connectGame(eServerType serverType) {

        allConnectSetMap.get(serverType).clear();

        InetSocketAddress address = new InetSocketAddress(YmlUtil.getString("netty.game.gameAddress"), YmlUtil.getInt("netty.game.port"));
        ServerClientGroup sessionGroup = new ServerClientGroup(address, null, NettyServices.LINK_COUNT, serverType);
        sessionGroup.setServerId(BuiltinPlugin.getServerId());
        boolean hasFailed = false;
        for (int i = 0; i < NettyServices.LINK_COUNT; i++) {
            try {
                NettyChannelSession session = sessionGroup.connect(i);
                if (session == null) {
                    logger.error("connect " + YmlUtil.getString("netty.game.gameAddress") + " , i = " + i + " fail. ");
                    hasFailed = true;
                    continue;
                }

                session.getChannel().attr(NettyServices.LINKED_CLIENT).set(sessionGroup);  //map里的值指向对象指针所有不用担心每个map不一样。
                sendRegisterServer(session, i, serverType);
            } catch (Exception e) {
                hasFailed = true;
                logger.error("connect " + YmlUtil.getString("netty.game.gameAddress") + " , i = " + i + " error. ", e);
            }
        }

        allConnectSetMap.get(serverType).put(YmlUtil.getInt("netty.agent.serverIndex"), sessionGroup);
        if (hasFailed) {
            ServerClientReConnectHelper.tryConnect(sessionGroup, 10);
        } else {
            logger.info("connect success：" + YmlUtil.getString("netty.game.gameAddress"));
        }
    }

    public static void sendRegisterServer(NettyChannelSession session, int linkIndex, eServerType serverType) {
        if (serverType == eServerType.GAME) {
            LoadProto.LoadMsg.Builder msg = writeProto(serverType, linkIndex);
            PBPacket packet = PBPacketUtils.buildMessage(GameProtocol.S_SERVER_REGISTER, msg);
            session.send(packet);
            return;
        }
        if (serverType == eServerType.CROSS) {
            LoadProto.LoadCrossMsg.Builder msg = writeProto(BuiltinPlugin.getServerId(), serverType,
                    linkIndex);
            PBPacket packet = PBPacketUtils.buildMessage(CrossProtocol.C_REGISTER_CROSS, msg);
            session.send(packet);
        }

    }

    //activityId对应跨服
    public static void routeCross(PBPacket packet) {
        try {
            CrossProto.BaseCrossReqMsg req = CrossProto.BaseCrossReqMsg.parseFrom(packet.getBytes());
            int activityId = req.getActivityId();
            if (activityId <= 0) {
                logger.error("can't find activty cross process. activtyId: {}, code: {}", activityId, packet.getCode());
                return;
            }
            Integer crossId = CrossActivityProcessMgr.getActivityCrossId(activityId);
            if (crossId == null) {
                logger.error("can't find activty cross process. activtyId: {}, code: {}", activityId, packet.getCode());
                return;
            }
            ServerClientGroup group = allConnectSetMap.get(eServerType.CROSS).get(crossId);
            if (group == null) {
                logger.error("activityId={} on crossId={}, not in this group, code: {}", activityId, crossId,
                        packet.getCode());
            }
            route(group, packet);
        } catch (InvalidProtocolBufferException e) {
            logger.error("parse cross req error. code: {}", packet.getCode());
        }

    }

    public static void routeClient(PBPacket packet) {
        AgentPlayer user = AgentPlayerMgr.getOnlineUser(packet.getPlayerId());
        if (user != null) {
            user.send(packet);
        } else {
            logger.debug("can no find player, userId: " + packet.getPlayerId() + ", code: " + packet.getCode());
        }
    }

    public static void routeGame(PBPacket packet) {
        ServerClientGroup group = allConnectSetMap.get(eServerType.GAME).get(0);
        route(group, packet);
    }

    private static void route(ServerClientGroup client, PBPacket packet) {
        if (client != null) {
            client.send(packet);
        }
    }

    public static LoadProto.LoadCrossMsg.Builder writeProto(long serverId,
            eServerType serverType, int linkIndex) {
        LoadProto.LoadCrossMsg.Builder msg = LoadProto.LoadCrossMsg.newBuilder();
        msg.setServerId(serverId);
        msg.setServerIndex(YmlUtil.getInt("netty.agent.serverIndex"));
        msg.setType(serverType.value);
        msg.setConnTimes(0);
        msg.setLinkIndex(linkIndex);
        return msg;
    }

    public static LoadProto.LoadMsg.Builder writeProto(eServerType serverType,
            int linkIndex) {
        LoadProto.LoadMsg.Builder msg = LoadProto.LoadMsg.newBuilder();
        msg.setServerIndex(YmlUtil.getInt("netty.agent.serverIndex"));
        msg.setType(serverType.value);
        msg.setConnTimes(0);
        msg.setLinkIndex(linkIndex);
        return msg;
    }

    public static void checkCrossConnect() {
        for (ServerClientGroup sessionGroup : allConnectSetMap.get(eServerType.CROSS).values()) {
            sessionGroup.keep();
        }
    }

    public static void checkNewCrossConnect() {
        logger.info("start check all conn, now conn info: {}", allConnectSetMap.get(eServerType.CROSS).keySet());
        int channelId = BuiltinPlugin.getChannelId();
        List<CrossServerInfo> crossServerInfoList = new CrossServerInfoDaoImpl()
                .getAllChannelCrossServerInfo(channelId);


        for (CrossServerInfo crossServerInfo : crossServerInfoList) {
            for (ProcessInfo info : crossServerInfo.getProcessInfoList()) {
                if (info == null) {
                    continue;
                }

                if (StringUtils.isBlank(info.getServerName())) {
                    continue;
                }

                if (!info.getServerName().startsWith("cross_server")) {
                    continue;
                }

                if (allConnectSetMap.get(eServerType.CROSS).containsKey(crossServerInfo.getCrossServerId())) {
                    LogUtil.COMMON.info("====保持跨服链接 {}", crossServerInfo.getCrossServerId());
                    continue;
                }

                LogUtil.COMMON.info("====连接全服跨 {}", JSON.toJSONString(info));
                connect1(crossServerInfo, info);


            }
        }

        logger.debug("all cross conn!");

    }

    public static void setCrossConnSetCheckTime(int index, int serverType, int serverId) {
        ServerClientGroup group = allConnectSetMap.get(eServerType.CROSS).get(serverId);
        if (group != null) {
            group.setSessionCheckTime(index);
        }
    }
}
